package com.ma.a15;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

/**
 * @ClassName A15
 * @Author: mayongqiang
 * @DATE 2022/4/11 20:36
 * @Description: Spring中jdk与cglib的统一
 */
/*
学到了什么
    a. Spring 的代理选择规则
    b. 底层的切点实现
    c. 底层的通知实现
    d. ProxyFactory 是用来创建代理的核心实现, 用 AopProxyFactory 选择具体代理实现
        - JdkDynamicAopProxy
        - ObjenesisCglibAopProxy
 */
public class A15 {

    public static void main(String[] args) {
        //1、备好切点
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution( * foo())");
        //2、备好通知
        MethodInterceptor advise = invocation -> {
            System.out.println("before");
            Object result = invocation.proceed();
            System.out.println("after");
            return result;
        };
        //3、备好切面
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advise);
        /* 4、创建代理
                a. proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
                b. proxyTargetClass = false,  目标没有实现接口, 用 cglib 实现
                c. proxyTargetClass = true, 总是使用 cglib 实现
         */
        //Target1 target1 = new Target1();
        Target2 target2 = new Target2();
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target2);
        factory.addAdvisor(advisor);
        factory.setInterfaces(target2.getClass().getInterfaces());
        factory.setProxyTargetClass(false);
        Target2 proxy = (Target2) factory.getProxy();
        System.out.println(proxy.getClass());

        proxy.foo();
        proxy.bar();

    }

    interface I1 {
        void foo();

        void bar();
    }

    static class Target1 implements I1 {
        public void foo() {
            System.out.println("target1 foo");
        }

        public void bar() {
            System.out.println("target1 bar");
        }
    }

    static class Target2 {
        public void foo() {
            System.out.println("target2 foo");
        }

        public void bar() {
            System.out.println("target2 bar");
        }
    }

    /*
    切点: 匹配规则
    通知: 增强逻辑
    切面: 组合切点与通知
        两个切面概念
        aspect =
            通知1(advice) +  切点1(pointcut)
            通知2(advice) +  切点2(pointcut)
            通知3(advice) +  切点3(pointcut)
            ...
        advisor = 更细粒度的切面，包含一个通知和切点
 */
    // 1、Aspect切面：最终生效之前会被拆解为advisor
    @Aspect
    static class MyAspect {

        // 前置通知@Before + 切点(execution)
        @Before("execution( * foo())")
        public void before() {
            System.out.println("before...");
        }

        // 后置通知@Before + 切点(execution)
        @After("execution( * foo())")
        public void after() {
            System.out.println("after...");
        }
    }

}
